`include "global_def.h"

module Fetch(
  I_CLOCK,
  I_LOCK,
  I_BranchPC,
  I_BranchAddrSelect,
  I_BranchStallSignal,
  I_DepStallSignal,
  I_RAST_STALL,
  O_LOCK,
  O_PC,
  O_IR,
  O_FetchStall
);

/////////////////////////////////////////
// IN/OUT DEFINITION GOES HERE
/////////////////////////////////////////
//
// Inputs from high-level module (lg_highlevel)
input I_CLOCK;
input I_LOCK;

// Inputs from the memory stage 
input [`PC_WIDTH-1:0] I_BranchPC; // Branch Target Address
input I_BranchAddrSelect; // Asserted only when Branch Target Address resolves

// Inputs from the decode stage
input I_BranchStallSignal; // Asserted from when branch instruction is decode to when Branch Target Address resolves 
input I_DepStallSignal; // Asserted when register dependency is detected
input I_RAST_STALL; //Stall from Rasterisation

// Outputs to the decode stage
output reg O_LOCK;
output reg [`PC_WIDTH-1:0] O_PC;
output reg [`IR_WIDTH-1:0] O_IR;

/////////////////////////////////////////
// ## Note ##
// O_FetchStall: Asserted when fetch stage is not updating FE/DE latch. 
// - The instruction with O_FetchStall == 1 will be treated as NOP in the following stages
/////////////////////////////////////////
output reg O_FetchStall; 
 
/////////////////////////////////////////
// WIRE/REGISTER DECLARATION GOES HERE
/////////////////////////////////////////
//
reg[`INST_WIDTH-1:0] InstMem[0:`INST_MEM_SIZE-1];
reg[`PC_WIDTH-1:0] PC;  

/////////////////////////////////////////
// INITIAL/ASSIGN STATEMENT GOES HERE
/////////////////////////////////////////
//
initial 
begin
  $readmemh("final.hex", InstMem);
  PC = 16'h0;

  O_LOCK = 1'b0;
  O_PC = 16'h0000;
  O_IR = 32'h00000000; //??? Why is it FF00000...nevermind i changed it
  O_FetchStall = 1'b0;
end

/////////////////////////////////////////
// ALWAYS STATEMENT GOES HERE
/////////////////////////////////////////
//

/////////////////////////////////////////
// ## Note ##
// 1. Update output values (O_FetchStall, O_PC, O_IR) and PC.
// 2. You should be careful about STALL signals.
/////////////////////////////////////////
always @(negedge I_CLOCK) //Update latch stuff
begin      
	if (!I_RAST_STALL) begin
	  O_LOCK <= I_LOCK;

	  if (!I_LOCK)
	  begin
		 PC <= 16'h0;
		 O_IR <= InstMem[0];
		 O_PC <= 16'h0;
		 O_FetchStall <= 1'b0;
	  end 
	  
	  else // (I_LOCK == 1)
	  begin
	  
		 //If DepStallSignal is enabled, don't do anything; leave latch values intact
		 if ((!I_DepStallSignal) && (!I_RAST_STALL)) 
		 begin //if it's not, we can do stuff
		 
			 if (I_BranchStallSignal) 
			 begin //Branch was detected in decode; stall until BrAddrSel == 1
				O_FetchStall <= 1'b1; //nop
			 end
			 
			 else if (O_FetchStall) 
			 begin //check to see if (BrAddrSel == 1), i.e. our new address to branch to is here
				 if (I_BranchAddrSelect) 
				 begin //It's here -- time to branch
					PC <= I_BranchPC + 16'h4;  // *** +4??? ***
					O_PC <= I_BranchPC + 16'h4; // *** +4??? Not +8??? *** Right. Probably.
					O_IR <= InstMem[I_BranchPC[`PC_WIDTH-1:2]];
					O_FetchStall <= 1'b0;
				 end
				 
				 else 
				 begin //It's not here yet -- keep passing nops
					O_FetchStall <= 1'b1;
				 end
			 end
			 
			 else 
			 begin //Not branching
				PC <= PC + 16'h4;  //*** Makes sense...***
				O_PC <= O_PC + 16'h4; // K....
				O_IR <= InstMem[PC[`PC_WIDTH-1:2]];
				O_FetchStall <= 1'b0;
			 end		 
		 end //if (!I_DepStallStallSignal)
		 
		 else //I_DepStallSignal == 1
		 begin
			O_FetchStall <= 1'b0;
			//do nothing else--leave latch values the same
		 end
		 
	  end // if (I_LOCK == 1)
  end // if (!I_RAST_STALL)
end // always @(negedge I_CLOCK)

endmodule // module Fetch
